local farming_stage_length = tonumber(minetest.settings:get("farming_stage_length"))

-- Side positions around stem and Rotation values
local faces = {
  [1] = {x = -1, z = 0, r = 3, o = 1, m = 14},
  [2] = {x = 1, z = 0, r = 1, o = 3, m = 16},
  [3] = {x = 0, z = -1, r = 2, o = 0, m = 5},
  [4] = {x = 0, z = 1, r = 0, o = 2, m = 11}
}

-------------
-- Helpers --
-------------


-- Find if a gourd already exist 
local function find_existing_gourd(pos, gourd_name)
  for side = 1, 4 do
    -- Get side node
    local side_pos = {
      x = pos.x + faces[side].x,
      y = pos.y,
      z = pos.z + faces[side].z
    }
    local side_node =  minetest.get_node(side_pos)

    -- If a gourd already exist, stop check
    if side_node.name == gourd_name and side_node.param2 == faces[side].m then
      return side
    end
  end

  return false
end

-- Find valid position for gourd
local function find_empty_spot(pos, gourd_name)
  -- Random check order
  local check_order = {1,2,3,4}
  for i = #check_order, 2, -1 do
    local j = math.random(i)
    check_order[i], check_order[j] = check_order[j], check_order[i]
  end

  -- Try to place gourd until a side is valid
  for i = 1, 4 do
    -- Get side node
    local side = check_order[i]
    local side_pos = {
      x = pos.x + faces[side].x,
      y = pos.y,
      z = pos.z + faces[side].z
    }
    local side_node =  minetest.get_node(side_pos)

    -- If gourd cannot be placed try next side
    if minetest.registered_nodes[side_node.name].drawtype ~= "plantlike"
    and minetest.registered_nodes[side_node.name].groups.flora ~= 1
    and side_node.name ~= "air" then
      break
    end

    -- Get node below side node
    local below_pos = {x = side_pos.x, y = side_pos.y - 1, z = side_pos.z}
    local below_node = minetest.get_node(below_pos)

    -- If gourd cannot be placed on solid surface try next side
    if minetest.registered_nodes[below_node.name].walkable == false then
      break
    end

    return side
  end

  return false
end

--------------------
-- Node Callbacks --
--------------------

function farming_stem_growth.stem_on_construct(stem_name, gourd_name, backup_crop_name)
  return function(pos)
    -- Only ensure the rotation is correct if a gourd already exist
    local existing_side = find_existing_gourd(pos, gourd_name)
    if existing_side then
      -- Swap and rotate stem
      minetest.swap_node(pos, {
        name = stem_name, param2 = faces[existing_side].r
      })

      return
    end

    -- Get side node
    local side = find_empty_spot(pos, gourd_name)

    if side then
      -- Get side node position
      local side_pos = {
        x = pos.x + faces[side].x,
        y = pos.y,
        z = pos.z + faces[side].z
      }

      -- Swap and rotate stem
      minetest.swap_node(pos, {
        name = stem_name, param2 = faces[side].r
      })

      -- Place gourd
      minetest.set_node(side_pos, {name = gourd_name, param2 = faces[side].m})


    else

      -- Revert back stem growing since the placing failed
      minetest.set_node(pos, {name = backup_crop_name})
    end
  end
end

function farming_stem_growth.gourd_on_dig(stem_name, post_dig_stem_name)
  return function(pos, node, digger)

    -- Loop through sides
    for face = 1, 4 do

      -- Get node at side
      local side_pos = {
        x = pos.x + faces[face].x,
        y = pos.y,
        z = pos.z + faces[face].z
      }
      local side_node = minetest.get_node(side_pos)

      -- If stem found that's attached, replace with previous stage
      if side_node.name == stem_name
      and side_node.param2 == faces[face].o then
        minetest.set_node(side_pos, {name = post_dig_stem_name})
      end
    end

    minetest.remove_node(pos)
  end
end

---------------
-- World Gen --
---------------

-- Gourd schematic (so we can spawn the gourd in world with a stem beside it)
function farming_stem_growth.schematic(stem_name, gourd_name)
  local _ = {name = "air", param1 = 0}
  local G = {name = gourd_name, param1 = 255, param2 = 11}
  local S = {name = stem_name, param1 = 255, param2 = 3}
  return {
    size = {x = 3, y = 2, z = 3},
    data = {
      _,_,_,
      _,G,S,
      _,_,_,
      _,_,_, _,_,_, _,_,_, -- padding
    }
  }
end